package com.syni.mdd.sjb.community.base.quartz;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.syni.mdd.sjb.common.component.mybatis.community.entity.*;
import com.syni.mdd.sjb.common.component.mybatis.community.mapper.SjbQywxChatGroupRefMapper;
import com.syni.mdd.sjb.common.component.mybatis.vo.ChatCountTotalVO;
import com.syni.mdd.sjb.community.base.constants.Constants;
import com.syni.mdd.sjb.community.base.entity.vo.QywxChatCountArrVO;
import com.syni.mdd.sjb.community.base.entity.vo.QywxChatCountVO;
import com.syni.mdd.sjb.community.base.entity.vo.QywxUserCountVO;
import com.syni.mdd.sjb.community.base.service.*;
import com.syni.mdd.sjb.community.base.utils.CommonBeanUtils;
import com.syni.mdd.sjb.community.base.utils.CommonUtils;
import com.syni.mdd.sjb.community.base.utils.OAUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author wangbaitao
 * @version 1.0.0
 * <h>企业微信定时任务</h>
 * @Date 2021/3/4
 **/
@Component
@Slf4j
public class QywxScheduleTask {
    @Autowired
    private SjbQywxCompanyUserService sjbQywxCompanyUserService;
    @Autowired
    private SjbUserCountService sjbUserCountService;
    @Autowired
    private SjbQywxExternalUserInfoService sjbQywxExternalUserInfoService;
    @Autowired
    private SjbQywxExternalUserRefService sjbQywxExternalUserRefService;
    @Autowired
    private SjbQywxChatGroupInfoService sjbQywxChatGroupInfoService;
    @Autowired
    private SjbChatCountService sjbChatCountService;
    @Resource
    private SjbQywxChatGroupRefMapper sjbQywxChatGroupRefMapper;
    @Autowired
    private SjbChatCountDetailsService sjbChatCountDetailsService;
    @Autowired
    private SjbQywxTagInfoService sjbQywxTagInfoService;

    /**
     * 前置提醒：
     * 以下所有同步依赖company表获取corpIds，再遍历同步，如果返回的请求体提示任务异常都只需要进行日志记录
     * <p>
     * 该接口需要进行每天凌晨3点30分的企业微信数据同步
     * 以下同步内容需要按照顺序进行同步：
     * <pre>
     *     1、根据company表进行企业员工的数据同步
     *     2、进行企业标签的数据同步
     *     3、根据company表进行外部联系人的全量数据同步
     *     4、根据compnay表进行企业群聊信息的全量数据同步
     *     5、根据company表进行统计数据的同步
     *     6、进行chat_count_details表的数据统计
     * </pre>
     * step 5 . 统计数据同步方法:
     * <pre>
     *      a)获取company表的corpId+permanent_code两个字段的数据
     *      b)获取company_user的所有员工数据，这里需要进行判断，如果企业员工超过100位，则需要进行递归处理(调用客户数据统计接口)
     *      根据此刻的外部联系人数据表进行total数据的统计
     *      c)获取company的所有群聊统计数据，并根据chat_ref表进行数据汇总
     * </pre>
     */
    public void syncQywxData(List<SjbQywxCompany> companies) {
        if (!companies.isEmpty()) {
            log.info("开始进行客户数据的同步任务，本次同步的企业信息有：{}家", companies.size());
            LocalDate dateParam = LocalDate.now().minusDays(1);
            //获取所有企业员工数据，并根据corpId进行分组
            Set<String> corpIds = companies.stream().map(SjbQywxCompany::getCorpId).collect(Collectors.toSet());
            //基础查询条件
            long beginDaySeconds = LocalDateTime.of(LocalDate.now().minusDays(1), LocalTime.MIN)
                    .toEpochSecond(OffsetDateTime.now().getOffset());
            long endDaySeconds = LocalDateTime.of(LocalDate.now().minusDays(1), LocalTime.MAX)
                    .toEpochSecond(OffsetDateTime.now().getOffset());
            //step 1 进行企业成员的数据同步，由前置任务自动完成
            //step 2 进行标签数据同步
            corpIds.forEach(corpId -> {
                try {
                    sjbQywxTagInfoService.syncData(corpId);
                } catch (Exception e) {
                    log.error("标签信息同步失败，corpId:{},err_msg:{}", corpId, e);
                }
            });
            log.info("企业标签同步完成，同步时间：{}", LocalDateTime.now());
            //获取企业成员数据
            List<SjbQywxCompanyUser> companyUsers = sjbQywxCompanyUserService.list(new QueryWrapper<SjbQywxCompanyUser>()
                    .in(SjbQywxCompanyUser.CORP_ID, corpIds));
            Map<String, List<SjbQywxCompanyUser>> companyUserMap = companyUsers.stream().collect(Collectors
                    .groupingBy(SjbQywxCompanyUser::getCorpid));
            //step 3 进行外部联系人的数据同步
            companies.forEach(company -> {
                if (Objects.nonNull(companyUserMap.get(company.getCorpId()))) {
                    for (SjbQywxCompanyUser companyUser : companyUserMap.get(company.getCorpId())) {
                        try {
                            sjbQywxExternalUserRefService.syncExternalUserData(companyUser, company);
                        } catch (Exception e) {
                            log.error("外部联系人同步失败，企业成员id:{}，错误信息：{}", companyUser.getUserid(), e);
                        }
                    }
                }
            });
            log.info("外部联系人数据同步完成，同步时间：{}", LocalDateTime.now());
            //step 4 进行群聊数据的同步
            companies.forEach(company -> {
                try {
                    sjbQywxChatGroupInfoService.syncData(company.getCorpId());
                } catch (Exception e) {
                    log.error("群聊数据同步失败，企业id:{}，错误信息：{}", company.getCorpId(), e);
                }
            });
            log.info("群聊数据同步完成，同步时间：{}", LocalDateTime.now());
            List<SjbQywxExternalUserInfo> userInfos = sjbQywxExternalUserInfoService
                    .list(new QueryWrapper<SjbQywxExternalUserInfo>().eq(SjbQywxExternalUserInfo.STATUS, false)
                            .in(SjbQywxExternalUserInfo.CORPID, corpIds));
            Map<String, List<SjbQywxExternalUserInfo>> userInfoMap = userInfos.stream()
                    .collect(Collectors.groupingBy(SjbQywxExternalUserInfo::getCorpid));
            //获取群聊统计total数据
            List<ChatCountTotalVO> chatCountTotalVOS = sjbQywxChatGroupRefMapper.getChatCountTotal(dateParam);
            Map<String, ChatCountTotalVO> chatCountTotalVOMap = chatCountTotalVOS.stream().collect(Collectors
                    .toMap(ChatCountTotalVO::getCorpid, Function.identity()));
            List<SjbUserCount> userCounts = new ArrayList<>();
            List<SjbChatCount> chatCounts = new ArrayList<>();
            companies.forEach(company -> {
                //step 5 进行统计数据的同步
                //获取客户统计数据,这里返回的对象还需要进行totalCount数据的额外加工
                try {
                    syncUserCountData(company, companyUserMap, beginDaySeconds, endDaySeconds, userInfoMap, userCounts);
                } catch (Exception e) {
                    log.error("客户统计数据同步失败，企业id:{}，错误信息：{}", company.getCorpId(), e);
                }
                //获取群聊统计数据，这里同样需要进行去重total等数据的额外加工
                //这里的统计数据按照日聚合接口进行同步
                try {
                    syncChatCountData(company, beginDaySeconds, endDaySeconds, chatCounts,
                            chatCountTotalVOMap.get(company.getCorpId()));
                } catch (Exception e) {
                    log.error("群聊数据同步失败，企业id:{}，错误信息：{}", company.getCorpId(), e);
                }
            });
            //进行群聊明细统计数据的加工
            List<SjbChatCountDetails> chatCountDetails = sjbQywxChatGroupRefMapper.getChatDetailsTotal(LocalDate.now()
                    .minusDays(1));
            Map<String, SjbChatCountDetails> chatCountDetailsMap = chatCountDetails.stream()
                    .collect(Collectors.toMap(SjbChatCountDetails::getChatId, Function.identity()));
            List<SjbQywxChatGroupInfo> chatGroupInfos = sjbQywxChatGroupInfoService.list();
            LocalDateTime currentTime = LocalDateTime.now();
            List<SjbChatCountDetails> resultChatCountList = new ArrayList<>();
            chatGroupInfos.forEach(info -> {
                SjbChatCountDetails sjbChatCountDetails = chatCountDetailsMap.get(info.getChatId());
                if (Objects.isNull(sjbChatCountDetails)) {
                    sjbChatCountDetails = new SjbChatCountDetails();
                }
                sjbChatCountDetails.setCorpid(info.getCorpId());
                sjbChatCountDetails.setChatId(info.getChatId());
                sjbChatCountDetails.setOwner(info.getOwner());
                sjbChatCountDetails.setCreateTime(currentTime);
                sjbChatCountDetails.setStatTime(dateParam);
                resultChatCountList.add(sjbChatCountDetails);
            });
            //进行前置性删除
            sjbChatCountDetailsService.remove(new QueryWrapper<SjbChatCountDetails>()
                    .eq(SjbChatCountDetails.STAT_TIME, dateParam));
            sjbChatCountDetailsService.saveBatch(resultChatCountList);
            //进行可重复操作的数据删除
            //删除用户统计数据
            //todo 这部分数据回头需要优化一下
            sjbUserCountService.remove(new QueryWrapper<SjbUserCount>().in(SjbUserCount.CORP_ID, corpIds)
                    .eq(SjbUserCount.STAT_TIME, dateParam));
            //删除群聊统计数据
            sjbChatCountService.remove(new QueryWrapper<SjbChatCount>().in(SjbChatCount.CORP_ID, corpIds)
                    .eq(SjbChatCount.STAT_TIME, dateParam));
            sjbChatCountService.saveBatch(chatCounts);
            sjbUserCountService.saveBatch(userCounts);
            log.info("客户统计和群聊统计数据同步完成，同步时间：{},进行持久的客户统计数据:{}条，群聊统计数据：{}条",
                    LocalDateTime.now(), userCounts.size(), chatCounts.size());
        }
    }

    /**
     * 进行群聊统计数据的同步
     *
     * @param company         企业信息
     * @param beginDaySeconds 时间区间-开始
     * @param endDaySeconds   时间区间-结束
     * @param chatCounts      需要加工的群聊统计集合
     */

    private void syncChatCountData(SjbQywxCompany company, long beginDaySeconds, long endDaySeconds,
                                   List<SjbChatCount> chatCounts, ChatCountTotalVO chatCountTotalVO) {
        JSONObject jsonObj = OAUtils.getChatCount(CommonBeanUtils.getToken(company.getCorpId()), beginDaySeconds,
                endDaySeconds, company.getCorpId());
        SjbChatCount sjbChatCount = new SjbChatCount();
        //判断返回是否正常
        if (Objects.equals(Constants.QYWX_OK, jsonObj.getInteger(Constants.ERROR_CODE))) {
            //进行数据的加工
            String items = jsonObj.getString(Constants.ITEMS);
            List<QywxChatCountArrVO> countArrVOS = JSONArray.parseArray(items, QywxChatCountArrVO.class);
            if (!countArrVOS.isEmpty()) {
                //进行加工
                QywxChatCountVO chatCountArrVO = countArrVOS.get(0).getData();
                sjbChatCount.setCreateTime(LocalDateTime.now());
                sjbChatCount.setStatTime(LocalDate.now().minusDays(1));
                sjbChatCount.setCorpid(company.getCorpId());
                sjbChatCount.setNewChatCnt(chatCountArrVO.getNew_chat_cnt());
                sjbChatCount.setChatTotal(chatCountArrVO.getChat_total());
                sjbChatCount.setChatHasMsg(chatCountArrVO.getChat_has_msg());
                sjbChatCount.setNewMemberCnt(chatCountArrVO.getNew_member_cnt());
                sjbChatCount.setMemberTotal(chatCountArrVO.getMember_total());
                sjbChatCount.setMemberHasMsg(chatCountArrVO.getMember_has_msg());
                sjbChatCount.setMsgTotal(chatCountArrVO.getMsg_total());
            }
        } else {
            log.error("获取客户统计数据失败，当前corpId:{},异常信息:{}", company.getCorpId(),
                    jsonObj.getString(Constants.ERROR_MSG));
        }
        if (Objects.nonNull(chatCountTotalVO)) {
            BeanUtils.copyProperties(chatCountTotalVO, sjbChatCount, "corpid");
        } else {
            sjbChatCount.setDistinctMemberTotal(0L);
            sjbChatCount.setDistinctCompanyUserTotal(0L);
            sjbChatCount.setNewDistinctMemberCnt(0L);
        }
        chatCounts.add(sjbChatCount);
    }

    /**
     * 进行客户统计数据同步
     *
     * @param company         企业信息
     * @param companyUserMap  企业成员Map
     * @param beginDaySeconds 时间区间-开始
     * @param endDaySeconds   时间区间-结束
     * @param userInfoMap     用户信息map
     * @param userCounts      需要加工的对象集合
     */
    private void syncUserCountData(SjbQywxCompany company, Map<String, List<SjbQywxCompanyUser>> companyUserMap,
                                   long beginDaySeconds, long endDaySeconds,
                                   Map<String, List<SjbQywxExternalUserInfo>> userInfoMap,
                                   List<SjbUserCount> userCounts) {
        SjbUserCount sjbUserCount = new SjbUserCount();
        sjbUserCount.setCorpid(company.getCorpId());
        sjbUserCount.setCreateTime(LocalDateTime.now());
        sjbUserCount.setStatTime(LocalDate.now().minusDays(1));
        if (Objects.nonNull(companyUserMap.get(company.getCorpId()))) {
            produceUserCountData(companyUserMap.get(company.getCorpId()), company, beginDaySeconds, endDaySeconds,
                    sjbUserCount);
        }
        //进行totalCount的赋值
        if (Objects.nonNull(userInfoMap.get(company.getCorpId()))) {
            sjbUserCount.setTotalCount((long) userInfoMap.get(company.getCorpId()).size());
        }
        userCounts.add(sjbUserCount);
    }

    /**
     * 进行客户数据统计
     *
     * @param companyUsers    企业成员
     * @param company         企业对象
     * @param beginDaySeconds 统计范围-起始
     * @param endDaySeconds   统计范围-结束
     */
    private void produceUserCountData(List<SjbQywxCompanyUser> companyUsers, SjbQywxCompany company,
                                      long beginDaySeconds, long endDaySeconds, SjbUserCount sjbUserCount) {
        List<String> userIds = companyUsers.stream().map(SjbQywxCompanyUser::getUserid).collect(Collectors.toList());
        List<List<String>> userIdPartition = CommonUtils.split(userIds, 100);
        List<JSONObject> userCountJsonObjects = new ArrayList<>();
        userIdPartition.forEach(userIdList -> {
            String token = CommonBeanUtils.getToken(company.getCorpId());
            JSONObject jsonObj = OAUtils.getUserCount(token, userIdList, beginDaySeconds, endDaySeconds, company.getCorpId());
            //判断返回是否正常
            if (Objects.equals(Constants.QYWX_OK, jsonObj.getInteger(Constants.ERROR_CODE))) {
                userCountJsonObjects.add(jsonObj);
            } else {
                log.error("获取客户统计数据失败，当前corpId:{},异常信息:{}", company.getCorpId(),
                        jsonObj.getString(Constants.ERROR_MSG));
            }
        });
        //进行当日的数据汇总
        if (!userCountJsonObjects.isEmpty()) {
            userCountJsonObjects.forEach(jsonObject -> {
                String count = jsonObject.getString(Constants.BEHAVIOR_DATA);
                //这里只需要获取第一个,然后加工到SjbUserCount对象中
                List<QywxUserCountVO> userCountVOS = JSONArray.parseArray(count, QywxUserCountVO.class);
                if (!userCountVOS.isEmpty()) {
                    //进行数据的加工
                    QywxUserCountVO countVO = userCountVOS.get(0);
                    sjbUserCount.setChatCnt(sjbUserCount.getChatCnt() + countVO.getChat_cnt());
                    sjbUserCount.setMessageCnt(sjbUserCount.getMessageCnt() + countVO.getMessage_cnt());
                    if (Objects.nonNull(countVO.getChat_cnt()) && Objects.nonNull(countVO.getReply_percentage())) {
                        sjbUserCount.setReplyChat(countVO.getReply_percentage() * countVO.getChat_cnt()
                                + sjbUserCount.getReplyChat());
                    }
                    sjbUserCount.setAvgReplyTime(sjbUserCount.getAvgReplyTime() + countVO.getAvg_reply_time());
                    sjbUserCount.setNegativeFeedbackCnt(sjbUserCount.getNegativeFeedbackCnt() +
                            countVO.getNegative_feedback_cnt());
                    sjbUserCount.setNewApplyCnt(sjbUserCount.getNewApplyCnt() + countVO.getNew_apply_cnt());
                    sjbUserCount.setNewContactCnt(sjbUserCount.getNewContactCnt() + countVO.getNew_contact_cnt());
                }
            });
        }
        //进行replyPercent的加工
        if (!Objects.equals(0L, sjbUserCount.getChatCnt())) {
            sjbUserCount.setReplyPercentage(sjbUserCount.getReplyChat() / sjbUserCount.getChatCnt());
        }
    }

}
